package pluginUtils;

import sliceformUtils.CoupleDAngles;
import sliceformUtils.DroiteSpace3D;
import sliceformUtils.Rotable;


public class Plane implements Rotable<Plane>{
	// ax+by+cz+d=0
	private double a,b,c,d; 
	
	
	public Plane(double x,double y,double z,double t){
		this.a=x; 
		this.b=y;
		this.c=z; 
		this.d=t; 
	}
	
	public Plane (Pos3D X, Pos3D Y, Pos3D Z){
		// vecteur XY
		Pos3D vec1=new Pos3D(Y.x-X.x,Y.y-X.y,Y.z-X.z); 
		//vecteur XZ
		Pos3D vec2=new Pos3D(Z.x-X.x,Z.y-X.y,Z.z-X.z);
		double rx=vec1.y*vec2.z-vec1.z*vec2.y; 
		double ry=-(vec1.x*vec2.z-vec1.z*vec2.x);
		double rz=vec1.x*vec2.y-vec1.y*vec2.x; 
		double rd=rx*X.x+ry*X.y+rz*X.z; 
		this.a=rx; 
		this.b=ry; 
		this.c=rz; 
		this.d=-rd; 
		
	}
	
	// Equation d'un plan connaissant le vecteur normal et un point
	public Plane(Pos3D Vec,Pos3D A){
		this(Vec.x,Vec.y,Vec.z,-Vec.produitScalaire(A)); 
	}
	
	static public Plane computePlane(Pos3D X, Pos3D Y, Pos3D Z){
		// vecteur XY
		Pos3D vec1=new Pos3D(Y.x-X.x,Y.y-X.y,Y.z-X.z); 
		//vecteur XZ
		Pos3D vec2=new Pos3D(Z.x-X.x,Z.y-X.y,Z.z-X.z);
		double rx=vec1.y*vec2.z-vec1.z*vec2.y; 
		double ry=-(vec1.x*vec2.z-vec1.z*vec2.x);
		double rz=vec1.x*vec2.y-vec1.y*vec2.x; 
		double rd=rx*X.x+ry*X.y+rz*X.z; 
		return new Plane(rx,ry,rz,-rd); 
	}
	
	/** Distance du point au plan (signee)**/
	public double relative(Pos3D X){
		return (a*X.x+b*X.y+c*X.z+d)/Math.sqrt(a*a+b*b+c*c); 
	}
	
	public double getA() {
		return a;
	}

	public double getB() {
		return b;
	}

	public double getC() {
		return c;
	}

	public double getD() {
		return d;
	}

	/** Intersection d'un segment de droite et d'un plan  **/
	public Pos3D pointInter(Pos3D X,Pos3D Y){
		// determiner alpha
		double numer=relative(Y); 
		double denom= relative(Y)-relative(X);
		double alpha=numer/denom; 
		Pos3D inter1=Pos3D.mul(X, alpha); 
		Pos3D inter2=Pos3D.mul(Y, 1-alpha); 
		return Pos3D.add(inter1,inter2); 
	}
	
	public Pos3D getVecteurNormal(){
		Pos3D emile=new Pos3D(a,b,c);
		emile.normalize(); 
		return emile; 
	}
	
	public String toString(){
		return a+"*X+"+b+"*Y+"+c+"*Z+"+d+"=0"; 
	}
	
	public String rawString(){
		return a+" "+b+" "+c+" "+d; 
	}
	
	public String toPovray(){
		return "plane{<"+-a+","+-b+","+-c+">,"+this.relative(Pos3D.ZERO)+"}"; 
	}

	public double distancePoint(Pos3D A){
		double ps=Math.abs(this.a*A.x+this.b*A.y+this.c*A.z+this.d); 
		return ps/Math.sqrt(a*a+b*b+c*c); 
	}
	
	// il faut une distance signee
	public Pos3D projectionOrthogonale(Pos3D A){
		double lambda=-this.relative(A); 
		lambda=lambda/Math.sqrt(a*a+b*b+c*c); 
		return new Pos3D(lambda*a+A.x,lambda*b+A.y,lambda*c+A.z); 
	}
	
	public static DroiteSpace3D inter(Plane p1,Plane p2){
		if((p1.getA()==0)&&(p2.getA()!=0)){
			Plane temp=p1; p1=p2; p2=temp;
		}
		if((p1.getA()==0)&&(p2.getA()==0)){ // droite de direction (1,0,0) (ou bien plan paralleles, pas de solution)
			if((p1.getB()==0)&&(p2.getB()==0)) return null; // plans paralleles
			if((p1.getB()==0)&&(p2.getB()!=0)){
				Plane temp=p1; p1=p2; p2=temp;
			}
			// Chercher un point (x,y,z)
			double bdm=p2.getB()*p1.getD()-p1.getB()*p2.getD(); 
			double cbm=p2.getC()*p1.getB()-p1.getC()*p2.getB(); 
			double zconstante=bdm/cbm;
			double yconstante=-p1.getC()/p1.getB()*zconstante-(p1.getD()/p1.getB()); 
			Pos3D lepoint=new Pos3D(0,yconstante,zconstante); 
			Pos3D ladirection=new Pos3D(1,0,0);
			return new DroiteSpace3D(lepoint,ladirection); 
		}
		
		double ab=p1.getA()*p2.getB()-p1.getB()*p2.getA(); 
		if(ab==0) // vecteur directeur dans le plan xOy (composante nulle selon z)
		{
			double bdm=p2.getB()*p1.getD()-p1.getB()*p2.getD(); 
			double cbm=p2.getC()*p1.getB()-p1.getC()*p2.getB(); 
			double zconstante=bdm/cbm;
			double yconstante=-1/p1.getB()*(p1.getC()*zconstante-p1.getD());
			Pos3D lepoint=new Pos3D(0,yconstante,zconstante);
			Pos3D ladirection=new Pos3D(1,-p1.getA()/p1.getB(),0); 
			return new DroiteSpace3D(lepoint,ladirection); 
		}
		double adm=p2.getA()*p1.getD()-p1.getA()*p2.getD(); 
		double acm=p2.getA()*p1.getC()-p1.getA()*p2.getC();
		double yconstante=adm/ab; 
		double zconstante=0; 
		double xconstante=-p1.getB()/p1.getA()*(adm/ab)-p1.getD()/p1.getA(); 
		double zcoef=1; 
		double ycoef=acm/ab; 
		double xcoef=-p1.getB()/p1.getA()*(acm/ab)-p1.getC()/p1.getA(); 
		Pos3D lepoint=new Pos3D(xconstante,yconstante,zconstante);
		Pos3D ladirection=new Pos3D(xcoef,ycoef,zcoef); 
		return new DroiteSpace3D(lepoint,ladirection); 
	}
	
	
	public static void main(String[] args) {
		Pos3D X=new Pos3D(1,1,1); 
		Pos3D Y=new Pos3D(0,1,0); 
		Pos3D Z=new Pos3D(0,0,1);
		Plane p=computePlane(X,Y,Z); 
		System.out.println(p); 
		CoupleDAngles cda=new CoupleDAngles(Math.PI/12,Math.PI/7); 
		Plane p1=p.rotate(cda); 
		System.out.println("P1 "+p1); 
		Plane p2=p1.rotateInverse(cda); 
		System.out.println("P2 "+p2); 
		System.out.println(inter(p,p2));
		
	}

	@Override
	public Plane rotate(CoupleDAngles cda) {
		Pos3D vecteur=new Pos3D(a,b,c); 
		vecteur=vecteur.rotate(cda); 
		// trouver un point du plan
		// On essaie (0,0,z)
		if(a!=0){
			Pos3D point=new Pos3D(-d/a,0,0);
			point=point.rotate(cda); 
			return new Plane(vecteur,point); 
		}
		if(b!=0){
			Pos3D point=new Pos3D(0,-d/b,0);
			point=point.rotate(cda); 
			return new Plane(vecteur,point); 
		}
		if(c!=0){ // toujours le cas ici
			Pos3D point=new Pos3D(0,0,-d/c);
			point=point.rotate(cda); 
			return new Plane(vecteur,point); 
		}
		return null;
	}
	

	@Override
	public Plane rotateInverse(CoupleDAngles cda) {
		Pos3D vecteur=new Pos3D(a,b,c); 
		vecteur=vecteur.rotateInverse(cda); 
		// trouver un point du plan
		// On essaie (0,0,z)
		if(a!=0){
			Pos3D point=new Pos3D(-d/a,0,0);
			point=point.rotateInverse(cda); 
			return new Plane(vecteur,point); 
		}
		if(b!=0){
			Pos3D point=new Pos3D(0,-d/b,0);
			point=point.rotateInverse(cda); 
			return new Plane(vecteur,point); 
		}
		if(c!=0){ // toujours le cas ici
			Pos3D point=new Pos3D(0,0,-d/c);
			point=point.rotateInverse(cda); 
			return new Plane(vecteur,point); 
		}
		return null;
	}
	
}



